package com.hongding.sealcenter.util;

import cn.com.hongding.seal.sign.alg.AlgorithmPair;
import cn.com.hongding.seal.sign.util.CertUtils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.codec.Base64Decoder;

import com.hongding.sealcenter.enums.AlgorithmOIDEnum;
import com.hongding.sealcenter.enums.SignAlgEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.UUID;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;


@Slf4j
public class CertificateUtils {

    static {
        // 加载BC密钥库
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.insertProviderAt(new BouncyCastleProvider(), 1);
        }
    }
    private static final String stdName="sm2p256v1";
    /**
     * 生成非对称密钥对
     * @param algorithm 密钥类型 rsa 2 sm2
     * @return 返回相应的非对称密钥pkcs8的base64编码
     */
    public static String getPkcs8(SignAlgEnum algorithm) throws RuntimeException {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = getPkcs8EncodedKeySpec(algorithm);
        return Base64.encode(pkcs8EncodedKeySpec.getEncoded());
    }

    /**
     * 生成证书请求
     *
     * @param dn 证书主题
     * @param pkcs8 非对称密钥对
     * @param algorithm 摘要算法 RSA:sha1 sha256 sha512  SM:sm3
     * @return 证书请求的base64编码
     */
    public static String getPkcs10(String dn, String pkcs8, SignAlgEnum algorithm)
        throws RuntimeException {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(pkcs8));
        return getPkcs10(dn, algorithm, pkcs8EncodedKeySpec);
    }

    /**
     * 生成证书请求
     *
     * @param dn 证书主题
     * @param algorithm 摘要算法 RSA:sha1 sha256 sha512  SM:sm3
     * @return 证书请求的base64编码
     */
    public static String getPkcs10(String dn,SignAlgEnum algorithm)throws RuntimeException{
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = getPkcs8EncodedKeySpec(algorithm);
        return getPkcs10(dn, algorithm, pkcs8EncodedKeySpec);
    }
    /**
     * 生成pkcs12
     *
     * @param signAlgEnum 签名算法
     * @param pkcs8 pkcs8的私钥数据base64编码
     * @param cert 证书base64编码
     * @param pwd pkcs12的保护密码
     * @return pkcs12base64编码
     */
    public static String getPkcs12(SignAlgEnum signAlgEnum,String pkcs8, String cert, String pwd)
        throws RuntimeException {
        if(StringUtils.isEmpty(cert)){
            throw new RuntimeException("生成PKCS12证书信息不能为空");
        }
        try (ByteArrayOutputStream store1Stream = new ByteArrayOutputStream();
             ByteArrayInputStream in2 = new ByteArrayInputStream(Base64.decode(cert))){
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(pkcs8));
            KeyFactory keyFactory = KeyFactory.getInstance(signAlgEnum.getAlgorithm(), "BC");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
            X509Certificate x509Certificate = (X509Certificate) cf.generateCertificate(in2);
            X509Certificate[] chain = new X509Certificate[1];
            chain[0] = x509Certificate;
            KeyStore store = KeyStore.getInstance("PKCS12", "BC");
            store.load(null, null);
            store.setKeyEntry(UUID.randomUUID().toString().replace("-", ""), privateKey, null, chain);
            store.store(store1Stream, pwd.toCharArray());
            return Base64.encode(store1Stream.toByteArray());
        } catch (Exception e) {
            log.error("生成PCKS12错误：{}",e.getMessage(),e);
            throw new RuntimeException("生成PCKS12错误");
        }
    }
    /**
     * 生成非对称密钥对
     * @param algorithm 密钥类型 rsa 2 sm2
     * @return 返回相应的非对称密钥pkcs8的base64编码
     */
    public static PKCS8EncodedKeySpec getPkcs8EncodedKeySpec(SignAlgEnum algorithm) throws RuntimeException {
        try{
            KeyPair pair = getPkcs8EncodedKeyPair(algorithm);
            return new PKCS8EncodedKeySpec(pair.getPrivate().getEncoded());
        }catch (Exception e){
            log.error("生成PKCS8错误：{}",e.getMessage(),e);
            throw new RuntimeException("生成PKCS8错误");
        }
    }
    /**
     * 生成非对称密钥对
     * @param algorithm 密钥类型 rsa 2 sm2
     * @return 返回相应的非对称密钥pkcs8的base64编码
     */
    public static KeyPair getPkcs8EncodedKeyPair(SignAlgEnum algorithm) throws RuntimeException {
        try{
            KeyPairGenerator generator=KeyPairGenerator.getInstance(algorithm.getAlgorithm(), "BC");
            if (SignAlgEnum.equalsRSA(algorithm.getCode())) {
                generator.initialize(algorithm.getLength(), new SecureRandom());
            } else if (SignAlgEnum.equalsSM2(algorithm.getCode())) {
                generator.initialize(new ECGenParameterSpec(stdName));
            } else {
                throw new Exception("不支持当前算法："+ algorithm);
            }
           return generator.generateKeyPair();
        }catch (Exception e){
            log.error("生成PKCS8错误：{}",e.getMessage(),e);
            throw new RuntimeException("生成PKCS8错误");
        }
    }
    /**
     * 生成证书请求
     *
     * @param dn 证书主题
     * @param pkcs8EncodedKeySpec 非对称密钥对
     * @param algorithm 摘要算法 RSA:sha1 sha256 sha512  SM:sm3
     * @return 证书请求的base64编码
     */
    private static String getPkcs10(String dn, SignAlgEnum algorithm, PKCS8EncodedKeySpec pkcs8EncodedKeySpec) throws RuntimeException {
        try{
            PKCS10CertificationRequest csr = getPKCS10CertificationRequest(dn, algorithm, pkcs8EncodedKeySpec);
            return Base64.encode(csr.getEncoded());
        }catch (Exception e){
            log.error("生成PKCS10错误：{}",e.getMessage(),e);
            throw new RuntimeException("生成PKCS10出现错误");
        }
    }
    /**
     * 生成证书请求
     *
     * @param dn 证书主题
     * @param pkcs8EncodedKeySpec 非对称密钥对
     * @param algorithm 摘要算法 RSA:sha1 sha256 sha512  SM:sm3
     * @return 证书请求的base64编码
     */
    public static PKCS10CertificationRequest getPKCS10CertificationRequest(String dn, SignAlgEnum algorithm,
                                                                            PKCS8EncodedKeySpec pkcs8EncodedKeySpec) throws RuntimeException {
        try{
            KeyFactory keyf = KeyFactory.getInstance(algorithm.getAlgorithm(), "BC");
            PrivateKey priv = keyf.generatePrivate(pkcs8EncodedKeySpec);
            PublicKey pubv;
            if (SignAlgEnum.equalsRSA(algorithm.getCode()))
            {
                RSAPrivateKey prikey = (RSAPrivateKey) priv;
                BigInteger publicExponent = new BigInteger("65537");
                RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(prikey.getModulus(), publicExponent);
                pubv = keyf.generatePublic(rsaPublicKeySpec);
            } else if (SignAlgEnum.equalsSM2(algorithm.getCode()))
            {
                BCECPrivateKey prikey = (BCECPrivateKey) priv;
                ECParameterSpec spec = prikey.getParameters();
                ECPoint q = spec.getG().multiply(prikey.getD()).normalize();
                ECNamedCurveParameterSpec namedCurve = ECNamedCurveTable.getParameterSpec(stdName);
                ECPublicKeySpec pubSpec = new ECPublicKeySpec(q, namedCurve);
                pubv = keyf.generatePublic(pubSpec);
            } else {
                throw new Exception("Not supported by the algorithm");
            }
            AlgorithmOIDEnum algorithmEnum=AlgorithmOIDEnum.buildSignAlg(algorithm);
            X500Name subject = new X500Name(dn);
            JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, pubv);
            ContentSigner signer = new JcaContentSignerBuilder(algorithmEnum.name()).setProvider("BC").build(priv);
            return builder.build(signer);
        }catch (Exception e){
            log.error("生成PKCS10错误：{}",e.getMessage(),e);
            throw new RuntimeException("生成PKCS10出现错误");
        }
    }
    /**
     * 解析证书
     */
    public static X509Certificate getX509Certificate(String certBase64) throws RuntimeException {
        return getX509Certificate(Base64Decoder.decode(certBase64));
    }
    /**
     * 解析证书
     */
    public static X509Certificate getX509Certificate(byte[] cert) throws RuntimeException {
        X509Certificate certificate;
        CertificateFactory certificatefactory;
        try (InputStream inputStream = new ByteArrayInputStream(cert)){
            certificatefactory = CertificateFactory.getInstance("X.509", "BC");
            certificate = (X509Certificate)certificatefactory.generateCertificate(inputStream);
        } catch (Exception e) {
            log.error("解析证书出现错误:{}",e.getMessage(),e);
            throw new RuntimeException(e.getMessage());
        }
        return certificate;
    }
    /**
     * 解析证书
     * @param fileCert 读取文件的byte类型
     */
    public static String getX509CertificateByFileBase64(byte[] fileCert) throws RuntimeException {
        X509Certificate certificate;
        CertificateFactory certificatefactory;
        try (InputStream inputStream = new ByteArrayInputStream(Base64Decoder.decode(fileCert))){
            certificatefactory = CertificateFactory.getInstance("X.509", "BC");
            certificate = (X509Certificate)certificatefactory.generateCertificate(inputStream);
            return Base64.encode(certificate.getEncoded());
        } catch (Exception e) {
            log.warn("解析文件流格式证书错误:"+e.getMessage());
            try {
                return Base64.encode(getX509Certificate(fileCert).getEncoded());
            } catch (Exception ex) {
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    /**
     * P10验证
     */
    public static  boolean validP10(String p10){
        try{
            PKCS10CertificationRequest pkcs10CertificationRequest = new PKCS10CertificationRequest(Base64.decode(p10));
            SubjectPublicKeyInfo subjectPublicKeyInfo = pkcs10CertificationRequest.getSubjectPublicKeyInfo();
            KeyFactory keyFact;
            log.info("当前P10的算法标识:{}",subjectPublicKeyInfo.getAlgorithm().getAlgorithm().getId());
            if(AlgorithmOIDEnum.RSA.getOid().equals(subjectPublicKeyInfo.getAlgorithm().getAlgorithm().getId())){
                keyFact = KeyFactory.getInstance(SignAlgEnum.RSA.getAlgorithm(), "BC");
            }else{
                keyFact = KeyFactory.getInstance("EC", "BC");
            }
            PublicKey p10Pub = keyFact.generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded()));
            log.info("公钥证书：{}",Base64.encode(p10Pub.getEncoded()));
            boolean signatureValid = pkcs10CertificationRequest.isSignatureValid((new JcaX509ContentVerifierProviderBuilder()).setProvider("BC").build(subjectPublicKeyInfo));
            log.info("P10验证结果:{}",signatureValid);
            return signatureValid;
        }catch (Exception e){
            log.error("验证P10发生错误:{}",e.getMessage(),e);
        }
        return false;
    }
    /**
     * 根据证书信息获取加密算法
     */
    public static AlgorithmPair parseDigestAndEncryptionName(Certificate cert) {
        String publicKeyAlgName = cert.getPublicKey().getAlgorithm();
        if (publicKeyAlgName.equals("EC")) {
            return new AlgorithmPair("SM3", "SM2");
        } else if (publicKeyAlgName.equals("RSA")) {
            return new AlgorithmPair("SHA256", "RSA");
        }
        throw new RuntimeException("获取摘要和加密算法异常");
    }

    /**
     * 解析证书信息以获取用户信息
     * @param certUser 包含证书信息的字符串
     * @return 证书中的用户信息（CN字段），如果不存在则返回null
     */
    public static String getCertUser(String certUser) {
        if(Strings.isEmpty(certUser)){
            return null;
        }
        String[] certificateFields = certUser.split(",");
        for (String s : certificateFields) {
            if (s.contains("CN=")) {
                return s.split("=")[1];
            }
        }
        return null;
    }


    public static void main(String[] args) {
        try {

            CertUtils.KeyStoreInfo keyStoreInfo = CertUtils.parseKeyStore(new File("C:\\Users\\admin\\Downloads\\0001测试企业1.pfx"), "1234");
            X509Certificate certificate = keyStoreInfo.getCertificate();
            log.info("证书序列号：{}", certificate.getSerialNumber().toString(16));
            log.info("证书颁发者：{}", certificate.getIssuerDN().toString());
            log.info("证书所有者：{}", certificate.getSubjectDN().toString());

        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
